//! Panic expression code generation (Go-style panic with recover support)

use super::super::LLVMBackend;
use crate::ast::types;
use crate::ast::*;
use crate::error::{CompilerError, Result};
use inkwell::types::BasicTypeEnum;
use inkwell::values::BasicValueEnum;

impl<'ctx> LLVMBackend<'ctx> {
    /// Generate panic expression (Go-style: panic(value))
    /// Go-like behavior:
    /// 1. Generate the panic value
    /// 2. Execute all defer statements (LIFO order) - defer may contain recover
    /// 3. Check if there's a recover handler in the recover stack (from defer)
    /// 4. Try pattern matching against recover arms
    /// 5. If matched, execute the handler and return to recover's continuation point (or return function value)
    /// 6. If not matched (and no wildcard), propagate panic upward
    pub(super) fn generate_panic_expression(
        &mut self,
        panic_expr: &expr::PanicExpr,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Generate the panic value
        let panic_value = self.generate_expression(&panic_expr.value)?;

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        // Step 1: Execute all defer statements (LIFO order)
        // Defer statements may contain recover calls that can catch this panic
        let defer_execute_block = self.context.append_basic_block(func, "panic_defer_execute");
        self.builder
            .build_unconditional_branch(defer_execute_block)?;
        self.builder.position_at_end(defer_execute_block);

        // Execute all pending defer statements
        // This mimics Go's behavior where defer is executed before recover check
        self.execute_all_defer_statements()?;

        // Step 2: After defer execution, check if recover caught the panic
        // Create blocks for recover checking
        let recover_check_block = self.context.append_basic_block(func, "panic_recover_check");
        let panic_propagate_block = self.context.append_basic_block(func, "panic_propagate");

        self.builder
            .build_unconditional_branch(recover_check_block)?;
        self.builder.position_at_end(recover_check_block);

        // Check if there's a recover handler that was registered during defer execution
        if let Some(recover_stmt) = self.recover_stack.last() {
            // Get the corresponding after_recover block where execution should continue
            let after_recover_block = *self.recover_after_blocks.last().ok_or_else(|| {
                CompilerError::internal("recover_stack and recover_after_blocks mismatch")
            })?;

            // Clone recover_stmt to avoid borrow checker issues
            let recover_stmt_clone = recover_stmt.clone();

            // Try to match against the recover arms
            // If matched, execution jumps to after_recover_block
            let matched =
                self.try_match_recover_arms(panic_value, &recover_stmt_clone, after_recover_block)?;

            // If matched, execution has already jumped to after_recover_block
            if !matched {
                // No match found, check for wildcard arm
                let wildcard_arm_opt = self.find_wildcard_arm(&recover_stmt_clone);
                if let Some(wildcard_arm) = wildcard_arm_opt {
                    // Clone the arm to avoid borrow issues
                    let arm_clone = wildcard_arm.clone();
                    let error_var = recover_stmt_clone.error_var.clone();
                    // Execute wildcard handler and jump to after_recover
                    self.execute_recover_arm(&arm_clone, panic_value, &error_var)?;
                    self.builder
                        .build_unconditional_branch(after_recover_block)?;
                } else {
                    // No wildcard, panic propagates upward
                    self.builder
                        .build_unconditional_branch(panic_propagate_block)?;
                }
            }
        } else {
            // No recover handler, panic propagates upward
            self.builder
                .build_unconditional_branch(panic_propagate_block)?;
        }

        // Position at panic_propagate_block (unreachable if panic was caught)
        self.builder.position_at_end(panic_propagate_block);
        // No recover handler or no match, terminate program
        self.call_panic_handler(panic_value)?;
        Ok(self.context.i32_type().const_int(0, false).into())
    }

    /// Try to match panic value against recover arms
    /// If a match is found, executes the handler and jumps to after_recover_block
    /// Returns true if a match was found and handler was executed (and jumped)
    fn try_match_recover_arms(
        &mut self,
        panic_value: BasicValueEnum<'ctx>,
        recover_stmt: &stmt::RecoverStmt,
        after_recover_block: inkwell::basic_block::BasicBlock<'ctx>,
    ) -> Result<bool> {
        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        // Create blocks
        let no_match_block = self.context.append_basic_block(func, "recover_no_match");
        let match_end_block = self.context.append_basic_block(func, "recover_match_end");

        // Track if a match was found (using an alloca for a flag)
        let match_found_alloca = self
            .builder
            .build_alloca(self.context.bool_type(), "match_found")?;
        self.builder.build_store(
            match_found_alloca,
            self.context.bool_type().const_int(0, false),
        )?;

        // Pre-populate no_match_block with a terminator BEFORE any branches to it
        // This ensures the block always has at least one instruction (the branch)
        // Critical: This must be done before any branches to no_match_block
        let saved_position = self.builder.get_insert_block();
        self.builder.position_at_end(no_match_block);
        self.builder.build_unconditional_branch(match_end_block)?;

        // Restore builder position
        if let Some(saved) = saved_position {
            self.builder.position_at_end(saved);
        }

        // Iterate through arms (except wildcard)
        let non_wildcard_arms: Vec<_> = recover_stmt
            .arms
            .iter()
            .enumerate()
            .filter(|(_, arm)| !self.is_wildcard_pattern(&arm.pattern))
            .collect();

        if non_wildcard_arms.is_empty() {
            // No non-wildcard arms, go directly to no_match
            self.builder.build_unconditional_branch(no_match_block)?;
        } else {
            // Iterate through non-wildcard arms
            let mut prev_next_block: Option<inkwell::basic_block::BasicBlock> = None;
            for (idx, (i, arm)) in non_wildcard_arms.iter().enumerate() {
                let pattern_check_block = if let Some(prev_block) = prev_next_block {
                    // Position at previous next_block (which was already created)
                    self.builder.position_at_end(prev_block);
                    // Create the pattern check block
                    let check_block = self
                        .context
                        .append_basic_block(func, &format!("pattern_check_{}", i));
                    // Branch to the check block
                    self.builder.build_unconditional_branch(check_block)?;
                    check_block
                } else {
                    // First pattern: branch from current position
                    let check_block = self
                        .context
                        .append_basic_block(func, &format!("pattern_check_{}", i));
                    // Branch to the check block from current position
                    self.builder.build_unconditional_branch(check_block)?;
                    check_block
                };

                let guard_check_block = self
                    .context
                    .append_basic_block(func, &format!("guard_check_{}", i));
                let handler_block = self
                    .context
                    .append_basic_block(func, &format!("handler_{}", i));
                let is_last = idx == non_wildcard_arms.len() - 1;

                // Position builder at pattern check block
                self.builder.position_at_end(pattern_check_block);

                // Try to match the pattern
                let match_result = self.match_pattern(&arm.pattern, panic_value)?;
                let target_if_no_match = if is_last {
                    no_match_block
                } else {
                    // Create next_pattern_block for next iteration
                    let next_block = self
                        .context
                        .append_basic_block(func, &format!("next_pattern_{}", i));
                    prev_next_block = Some(next_block);
                    next_block
                };
                self.builder.build_conditional_branch(
                    match_result,
                    guard_check_block,
                    target_if_no_match,
                )?;

                // Pattern matched: check guard if present
                self.builder.position_at_end(guard_check_block);
                if let Some(ref guard) = arm.guard {
                    let guard_result = self.generate_expression(guard)?;
                    let guard_bool = self.is_truthy_value(guard_result)?;
                    self.builder.build_conditional_branch(
                        guard_bool,
                        handler_block,
                        target_if_no_match,
                    )?;
                } else {
                    // No guard, go directly to handler
                    self.builder.build_unconditional_branch(handler_block)?;
                }

                // Execute handler
                self.builder.position_at_end(handler_block);
                let error_var = recover_stmt.error_var.clone();
                self.execute_recover_arm(arm, panic_value, &error_var)?;
                // After handler executes, jump to after_recover_block (not match_end_block)
                // This skips panic and all subsequent code
                self.builder
                    .build_unconditional_branch(after_recover_block)?;
            }

            // If there's a leftover next_block (shouldn't happen if logic is correct), branch to no_match
            if let Some(next_block) = prev_next_block {
                self.builder.position_at_end(next_block);
                self.builder.build_unconditional_branch(no_match_block)?;
            }
        }

        // Ensure no_match_block has a terminator
        // This is critical: if no_match_block was branched to but doesn't have instructions, add terminator
        if no_match_block.get_terminator().is_none() {
            self.builder.position_at_end(no_match_block);
            self.builder.build_unconditional_branch(match_end_block)?;
        }

        // Position at match_end_block (reached if no match found)
        // If we reach here, no arm matched and execution should continue normally (or check wildcard)
        // Note: If a handler executed, it already jumped to after_recover_block
        self.builder.position_at_end(match_end_block);

        // Return false to indicate no match (handlers already jumped away)
        Ok(false)
    }

    /// Find wildcard arm (_) in recover statement
    fn find_wildcard_arm<'a>(
        &self,
        recover_stmt: &'a stmt::RecoverStmt,
    ) -> Option<&'a stmt::RecoverArm> {
        recover_stmt
            .arms
            .iter()
            .find(|arm| self.is_wildcard_pattern(&arm.pattern))
    }

    /// Check if pattern is a wildcard
    fn is_wildcard_pattern(&self, pattern: &expr::Pattern) -> bool {
        matches!(pattern, expr::Pattern::Wildcard)
    }

    /// Match pattern against panic value
    /// Returns an IntValue (i1) representing whether the pattern matches (1 = match, 0 = no match)
    fn match_pattern(
        &mut self,
        pattern: &expr::Pattern,
        panic_value: BasicValueEnum<'ctx>,
    ) -> Result<inkwell::values::IntValue<'ctx>> {
        match pattern {
            expr::Pattern::Wildcard => {
                // Wildcard always matches
                Ok(self.context.bool_type().const_int(1, false))
            }
            expr::Pattern::Literal(lit) => {
                // Match against literal value
                let lit_expr = Expression::Literal(lit.clone());
                let lit_value = self.generate_expression(&lit_expr)?;
                self.compare_values(panic_value, lit_value)
            }
            expr::Pattern::Variable(_) => {
                // Variable pattern always matches
                Ok(self.context.bool_type().const_int(1, false))
            }
            expr::Pattern::Struct(struct_pattern) => {
                // Match struct type and fields
                self.match_struct_pattern(struct_pattern, panic_value)
            }
            expr::Pattern::Or(patterns) => {
                // Try to match any of the patterns (OR logic)
                // Create blocks for OR matching
                let func = self.current_function.ok_or_else(|| {
                    CompilerError::internal("Cannot create blocks outside function")
                })?;
                let mut match_blocks = Vec::new();
                let no_match_block = self.context.append_basic_block(func, "or_no_match");
                let match_block = self.context.append_basic_block(func, "or_match");

                // For each pattern, create a comparison block
                for (i, pattern) in patterns.iter().enumerate() {
                    let cmp_block = self
                        .context
                        .append_basic_block(func, &format!("or_cmp_{}", i));
                    if i == 0 {
                        // First comparison: branch from current position
                        let cmp_result = self.match_pattern(pattern, panic_value)?;
                        self.builder.build_conditional_branch(
                            cmp_result,
                            match_block,
                            cmp_block,
                        )?;
                    } else {
                        // Subsequent comparisons: branch from previous no-match block
                        let &prev_block = match_blocks.last().unwrap();
                        self.builder.position_at_end(prev_block);
                        let cmp_result = self.match_pattern(pattern, panic_value)?;
                        self.builder.build_conditional_branch(
                            cmp_result,
                            match_block,
                            cmp_block,
                        )?;
                    }
                    match_blocks.push(cmp_block);
                }

                // All patterns failed: go to no_match from the last comparison block
                if !match_blocks.is_empty() {
                    if let Some(&last_block) = match_blocks.last() {
                        self.builder.position_at_end(last_block);
                        self.builder.build_unconditional_branch(no_match_block)?;
                    }
                } else {
                    // Empty pattern list - should not happen, but handle gracefully
                    // Go directly to no_match
                    self.builder.build_unconditional_branch(no_match_block)?;
                }

                // No match block: return 0
                self.builder.position_at_end(no_match_block);
                let no_match_val = self.context.bool_type().const_int(0, false);
                self.builder.build_unconditional_branch(match_block)?;

                // Match block: return 1 using PHI node
                self.builder.position_at_end(match_block);
                let phi = self
                    .builder
                    .build_phi(self.context.bool_type(), "or_match_phi")?;
                let match_val = self.context.bool_type().const_int(1, false);
                for &cmp_block in &match_blocks {
                    phi.add_incoming(&[(&match_val, cmp_block)]);
                }
                phi.add_incoming(&[(&no_match_val, no_match_block)]);

                Ok(phi.as_basic_value().into_int_value())
            }
            _ => {
                // Other patterns not yet supported (Type pattern removed, use struct pattern instead)
                Ok(self.context.bool_type().const_int(0, false))
            }
        }
    }

    /// Compare two values for equality
    /// Returns an IntValue (i1) representing equality (1 = equal, 0 = not equal)
    fn compare_values(
        &mut self,
        val1: BasicValueEnum<'ctx>,
        val2: BasicValueEnum<'ctx>,
    ) -> Result<inkwell::values::IntValue<'ctx>> {
        match (val1, val2) {
            (BasicValueEnum::IntValue(int1), BasicValueEnum::IntValue(int2)) => {
                // Integer comparison
                let cmp = self.builder.build_int_compare(
                    inkwell::IntPredicate::EQ,
                    int1,
                    int2,
                    "int_eq",
                )?;
                Ok(cmp)
            }
            (BasicValueEnum::FloatValue(float1), BasicValueEnum::FloatValue(float2)) => {
                // Float comparison
                let cmp = self.builder.build_float_compare(
                    inkwell::FloatPredicate::OEQ,
                    float1,
                    float2,
                    "float_eq",
                )?;
                Ok(cmp)
            }
            (BasicValueEnum::PointerValue(ptr1), BasicValueEnum::PointerValue(ptr2)) => {
                // Check if both are strings and compare content
                let val1: BasicValueEnum = ptr1.into();
                let val2: BasicValueEnum = ptr2.into();

                // Check if both operands are strings (i8*)
                if self.is_string_type(&val1) && self.is_string_type(&val2) {
                    // String content comparison using strcmp
                    let cmp_result = self.generate_string_content_compare(val1, val2)?;
                    // generate_string_content_compare returns 1 if equal, 0 if not equal
                    let one = self.context.i32_type().const_int(1, false);
                    let cmp = self.builder.build_int_compare(
                        inkwell::IntPredicate::EQ,
                        cmp_result.into_int_value(),
                        one,
                        "string_eq_check",
                    )?;
                    Ok(cmp)
                } else {
                    // Pointer comparison (non-string pointers)
                    let int1 =
                        self.builder
                            .build_ptr_to_int(ptr1, self.context.i64_type(), "ptr1_int")?;
                    let int2 =
                        self.builder
                            .build_ptr_to_int(ptr2, self.context.i64_type(), "ptr2_int")?;
                    let cmp = self.builder.build_int_compare(
                        inkwell::IntPredicate::EQ,
                        int1,
                        int2,
                        "ptr_eq",
                    )?;
                    Ok(cmp)
                }
            }
            _ => {
                // Different types, not equal
                Ok(self.context.bool_type().const_int(0, false))
            }
        }
    }

    /// Match struct pattern
    fn match_struct_pattern(
        &mut self,
        _struct_pattern: &expr::StructPattern,
        _panic_value: BasicValueEnum<'ctx>,
    ) -> Result<inkwell::values::IntValue<'ctx>> {
        // TODO: Implement struct pattern matching
        Ok(self.context.bool_type().const_int(0, false))
    }

    /// Check if value is truthy (for guard conditions)
    fn is_truthy_value(
        &mut self,
        value: BasicValueEnum<'ctx>,
    ) -> Result<inkwell::values::IntValue<'ctx>> {
        match value {
            BasicValueEnum::IntValue(int_val) => {
                // Check if int is non-zero
                let zero = int_val.get_type().const_int(0, false);
                Ok(self.builder.build_int_compare(
                    inkwell::IntPredicate::NE,
                    int_val,
                    zero,
                    "truthy_check",
                )?)
            }
            BasicValueEnum::FloatValue(float_val) => {
                // Check if float is non-zero
                let zero = float_val.get_type().const_float(0.0);
                Ok(self.builder.build_float_compare(
                    inkwell::FloatPredicate::ONE,
                    float_val,
                    zero,
                    "truthy_check",
                )?)
            }
            _ => {
                // Default to truthy for other types
                Ok(self.context.bool_type().const_int(1, false))
            }
        }
    }

    /// Execute recover arm handler
    fn execute_recover_arm(
        &mut self,
        arm: &stmt::RecoverArm,
        panic_value: BasicValueEnum<'ctx>,
        error_var: &str,
    ) -> Result<()> {
        // Set the error variable to the panic value
        // Allocate space for the error variable if it doesn't exist
        // CRITICAL: alloca must be in the entry block, not in the current block
        if !self.variable_map.contains_key(error_var) {
            // Get the type of the panic value
            let llvm_type: BasicTypeEnum<'ctx> = match panic_value {
                BasicValueEnum::IntValue(val) => val.get_type().into(),
                BasicValueEnum::FloatValue(val) => val.get_type().into(),
                BasicValueEnum::PointerValue(val) => val.get_type().into(),
                _ => self.context.i32_type().into(),
            };

            // Create alloca for the error variable in the entry block
            let fn_value = self.current_function.ok_or_else(|| {
                CompilerError::internal("Cannot allocate variable outside of function")
            })?;
            let entry_block = fn_value
                .get_first_basic_block()
                .ok_or_else(|| CompilerError::internal("Function has no entry block"))?;

            // Save current builder position
            let saved_position = self.builder.get_insert_block();

            // Position builder at the beginning of entry block (before any terminator)
            // Alloca instructions MUST be in the entry block and before any terminator
            // Get the first instruction in the entry block
            if let Some(first_inst) = entry_block.get_first_instruction() {
                // If entry block has instructions, insert alloca before the first instruction
                self.builder.position_before(&first_inst);
            } else {
                // If entry block is empty, position at the end (it will be the first instruction)
                self.builder.position_at_end(entry_block);
            }

            // Allocate space for the error variable
            let alloca = self.builder.build_alloca(llvm_type, error_var)?;

            // Restore builder position
            if let Some(saved) = saved_position {
                self.builder.position_at_end(saved);
            } else {
                // If no saved position, position at the end of entry block
                self.builder.position_at_end(entry_block);
            }

            // Store the variable in the map
            self.variable_map
                .insert(error_var.to_string(), alloca.into());

            // Infer the type from the panic value
            // For now, use a simple type inference
            // TODO: Properly infer the NRC type from panic_value
            let nrc_type = self.infer_type_from_llvm_value(&panic_value)?;
            self.variable_type_map
                .insert(error_var.to_string(), nrc_type);
        }

        // Store the panic value into the error variable
        if let Some(var_alloca) = self.variable_map.get(error_var) {
            if var_alloca.is_pointer_value() {
                let ptr = var_alloca.into_pointer_value();
                let _ = self.builder.build_store(ptr, panic_value);
            }
        }

        // Execute the handler body
        // Call generate_statement directly using the full path
        crate::llvm_backend::LLVMBackend::generate_statement(self, &arm.body)?;

        Ok(())
    }

    /// Infer NRC type from LLVM value (simple implementation)
    fn infer_type_from_llvm_value(&self, value: &BasicValueEnum<'ctx>) -> Result<Type> {
        match value {
            BasicValueEnum::IntValue(int_val) => {
                let width = int_val.get_type().get_bit_width();
                match width {
                    8 => Ok(Type::Basic(types::BasicType::Int8)),
                    16 => Ok(Type::Basic(types::BasicType::Int16)),
                    32 => Ok(Type::Basic(types::BasicType::Int32)),
                    64 => Ok(Type::Basic(types::BasicType::Int)),
                    _ => Ok(Type::Basic(types::BasicType::Int)),
                }
            }
            BasicValueEnum::FloatValue(_float_val) => {
                // Float types don't have get_bit_width
                // For now, assume all floats are f64
                // TODO: Distinguish between f32 and f64
                Ok(Type::Basic(types::BasicType::Float64))
            }
            BasicValueEnum::PointerValue(_) => {
                // Assume String type for pointers (most common case for panic values)
                Ok(Type::Basic(types::BasicType::String))
            }
            _ => Ok(Type::Basic(types::BasicType::Int)),
        }
    }

    /// Call panic handler (terminate program)
    fn call_panic_handler(&self, _panic_value: BasicValueEnum<'ctx>) -> Result<()> {
        // For now, just generate a call to exit or abort
        // TODO: Implement proper panic handler
        // This could call a standard library function or use LLVM's abort intrinsic

        // Use printf to print error message (for debugging)
        // Then call exit(1)

        // Get exit function
        let exit_fn = self
            .module
            .get_function("exit")
            .ok_or_else(|| CompilerError::internal("exit function not found"))?;

        // Call exit(1)
        let exit_code = self.context.i32_type().const_int(1, false);
        self.builder
            .build_call(exit_fn, &[exit_code.into()], "panic_exit")?;

        // Add unreachable instruction since exit never returns
        self.builder.build_unreachable()?;

        Ok(())
    }
}
